<template>
  <div class="dv-scroll-board" ref="scrollBoard">
    <div class="header" v-if="header.length && mergedConfigR" :style="`background-color: ${mergedConfigR.headerBGC};`">
      <div class="header-item" v-for="(headerItem, i) in header" :key="`${headerItem}${i}`" :style="`
          font-size:${mergedConfigR.headerFontSize}px;
          height: ${mergedConfigR.headerHeight}px;
          line-height: ${mergedConfigR.headerHeight}px;
          width: ${widths[i]}px;
        `" :align="aligns[i]" v-html="headerItem" />
    </div>

    <div v-if="heights.length" class="rows" :style="`height: ${height - (header.length ? mergedConfigR.headerHeight : 0)}px;`">
      <div class="row-item" v-for="(row, ri) in rows" :key="`${row.toString()}${row.scroll}`" :style="`
          height: ${heights[ri].value}px;
          line-height: ${heights[ri].value}px;
          background: ${mergedConfigR[sIndex===row.rowIndex?'selectedBGC':row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};`">
        <div class="ceil" v-for="(ceil, ci) in row.ceils" :key="`${ceil}${ri}${ci}`" :style="`width: ${widths[ci]}px;`" :align="aligns[ci]" v-html="ceil" @click="emitEvent('selected', ri, ci, row, ceil)" @mouseenter="handleHover(true, ri, ci, row, ceil)" @mouseleave="handleHover(false)">
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import autoResize from '../../autoResize';
import { defineComponent, watch, ref, onMounted, Ref } from 'vue';
import { deepMerge } from '@jiaminghi/charts/lib/util/index';
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util';

type Rows = {
	rowIndex: number;
	scroll: any;
	[x: string]: any;
};

export default defineComponent({
	name: 'DvScrollBoard',
	props: {
		config: {
			type: Object,
			default: () => ({}),
		},
		sIndex: Number,
	},
	setup(props, { emit }) {
		let animationIndex = ref(0);
		let animationHandler: NodeJS.Timeout | null = null;
		let updater = ref(0);
		let mergedConfigR: any = ref(null);
		let defaultConfig: any = ref({
			/**
			 * @description Board header
			 * @type {Array<String>}
			 * @default header = []
			 * @example header = ['column1', 'column2', 'column3']
			 */
			header: [],
			/**
			 * @description Board data
			 * @type {Array<Array>}
			 * @default data = []
			 */
			data: [],
			/**
			 * @description Row num
			 * @type {Number}
			 * @default rowNum = 5
			 */
			rowNum: 5,
			/**
			 * @description Header background color
			 * @type {String}
			 * @default headerBGC = '#00BAFF'
			 */
			headerBGC: '#00BAFF',
			/**
			 * @description Header font size
			 * @type {String}
			 * @default headerFontSize = 14
			 */
			headerFontSize: 14,
			/**
			 * @description Odd row background color
			 * @type {String}
			 * @default oddRowBGC = '#003B51'
			 */
			oddRowBGC: '#003B51',
			/**
			 * @description Even row background color
			 * @type {String}
			 * @default evenRowBGC = '#003B51'
			 */
			evenRowBGC: '#0A2732',
			/**
			 * @description click row background color
			 * @type {String}
			 * @default selectedBGC = 'red'
			 */
			selectedBGC: 'red',
			/**
			 * @description Scroll wait time
			 * @type {Number}
			 * @default waitTime = 2000
			 */
			waitTime: 2000,
			/**
			 * @description Header height
			 * @type {Number}
			 * @default headerHeight = 35
			 */
			headerHeight: 35,
			/**
			 * @description Column width
			 * @type {Array<Number>}
			 * @default columnWidth = []
			 */
			columnWidth: [],
			/**
			 * @description Column align
			 * @type {Array<String>}
			 * @default align = []
			 * @example align = ['left', 'center', 'right']
			 */
			align: [],
			/**
			 * @description Show index
			 * @type {Boolean}
			 * @default index = false
			 */
			index: false,
			/**
			 * @description index Header
			 * @type {String}
			 * @default indexHeader = '#'
			 */
			indexHeader: '#',
			/**
			 * @description Carousel type
			 * @type {String}
			 * @default carousel = 'single'
			 * @example carousel = 'single' | 'page'
			 */
			carousel: 'single',
			/**
			 * @description Pause scroll when mouse hovered
			 * @type {Boolean}
			 * @default hoverPause = true
			 * @example hoverPause = true | false
			 */
			hoverPause: true,
		});

		let header = ref([]);
		let rowsData = ref([]);
		let rows = ref([] as Rows[]);
		let width = ref(0);
		let height = ref(0);
		let widths = ref([] as any[]);
		let heights = ref([] as any[]);
		let avgHeight = ref(0);
		let aligns = ref([]);
		let needCalc = ref(false);
		let scrollBoard: Ref<HTMLElement | null> = ref(null);

		const animation = async (start = false) => {
			if (needCalc.value) {
				calcRowsData();
				calcHeights();
				needCalc.value = false;
			}
			const { waitTime, carousel, rowNum } = mergedConfigR.value;
			const rowLength = rowsData.value.length;
			if (rowNum >= rowLength) return;
			if (start) {
				await new Promise(resolve => setTimeout(resolve, waitTime));
				if (updater.value !== updater.value) return;
			}
			const animationNum = carousel === 'single' ? 1 : rowNum;
			let rowss = rowsData.value.slice(animationIndex.value);
			rowss.push(...rowsData.value.slice(0, animationIndex.value));

			rows.value = rowss.slice(
				0,
				carousel === 'page' ? rowNum * 2 : rowNum + 1,
			);
			heights.value = new Array(rowLength).fill(avgHeight.value);

			await new Promise(resolve => setTimeout(resolve, 300));
			if (updater.value !== updater.value) return;

			heights.value.splice(0, animationNum, ...new Array(animationNum).fill(0));

			animationIndex.value += animationNum;

			const back = animationIndex.value - rowLength;
			if (back >= 0) animationIndex.value = back;

			// this.animationIndex = animationIndex;
			animationHandler = setTimeout(animation, waitTime - 300);
		};

		const stopAnimation = () => {
			updater.value = (updater.value + 1) % 999999;
			if (animationHandler) {
				clearTimeout(animationHandler);
			}
		};
		const mergeConfig = () => {
			mergedConfigR.value = deepMerge(
				deepClone(defaultConfig.value, true),
				props.config || {},
			);
		};
		const calcHeaderData = () => {
			let { header: configHeader, index, indexHeader } = mergedConfigR.value;
			if (!configHeader.length) {
				header.value = [];
				return;
			}
			configHeader = [...configHeader];
			if (index) configHeader.unshift(indexHeader);
			header.value = configHeader;
		};
		const calcRowsData = () => {
			let { data, index, headerBGC, rowNum } = mergedConfigR.value;
			// data = this.data || data;
			if (index) {
				data = data.map((row: any, i: number) => {
					row = [...row];
					const indexTag = `<span class="index" style="background-color: ${headerBGC};">${
						i + 1
					}</span>`;
					row.unshift(indexTag);

					return row;
				});
			}

			data = data.map((ceils: any, i: number) => ({ ceils, rowIndex: i }));

			const rowLength = data.length;

			if (rowLength > rowNum && rowLength < 2 * rowNum) {
				data = [...data, ...data];
			}

			data = data.map((d: any, i: number) => ({ ...d, scroll: i }));

			rowsData.value = data;
			rows.value = data;
		};
		const calcWidths = () => {
			const { columnWidth, header } = mergedConfigR.value;

			const usedWidth = columnWidth.reduce(
				(all: number, w: number) => all + w,
				0,
			);

			let columnNum = 0;
			if (rowsData.value[0]) {
				columnNum = (rowsData.value[0] as any).ceils.length;
			} else if (header.length) {
				columnNum = header.length;
			}

			const avgWidth =
				(width.value - usedWidth) / (columnNum - columnWidth.length);

			const widthss = new Array(columnNum).fill(avgWidth);

			widths.value = deepMerge(widthss, columnWidth);
		};
		const calcHeights = (onresize = false) => {
			const { headerHeight, rowNum, data } = mergedConfigR.value;

			let allHeight = height.value;

			if (header.value.length) allHeight -= headerHeight;

			avgHeight.value = allHeight / rowNum;

			if (!onresize)
				heights.value = new Array(data.length).fill(avgHeight) as any[];
		};
		const calcAligns = () => {
			const columnNum = header.value.length;
			let alignss = new Array(columnNum).fill('left');
			const { align } = mergedConfigR.value;
			aligns.value = deepMerge(alignss, align);
		};
		const calcData = () => {
			mergeConfig();

			calcHeaderData();

			calcRowsData();

			calcWidths();

			calcHeights();

			calcAligns();

			animation(true);
		};
		const handleHover = (
			enter: boolean,
			ri?: any,
			ci?: any,
			row?: any,
			ceil?: any,
		) => {
			if (enter) emitEvent('mouseover', ri, ci, row, ceil);
			if (!mergedConfigR.value.hoverPause) return;

			if (enter) {
				stopAnimation();
			} else {
				animation(true);
			}
		};
		const afterAutoResizeMixinInit = () => {
			calcData();
		};
		const onResize = () => {
			if (!mergedConfigR.value) return;

			calcWidths();

			calcHeights();
		};
		const emitEvent = (type: any, ri: any, ci: any, row: any, ceil: any) => {
			const { ceils, rowIndex } = row;
			emit(type, {
				row: ceils,
				ceil,
				rowIndex,
				columnIndex: ci,
			});
		};
		const updateRows = (rows: any, aIndex: number) => {
			mergedConfigR.value = {
				...mergedConfigR.value,
				data: [...rows],
			};

			needCalc.value = true;

			if (typeof aIndex === 'number') animationIndex.value = aIndex;
			if (!animationHandler) animation(true);
		};
		watch(
			() => props.config,
			(n, o) => {
				stopAnimation();
				animationIndex.value = 0;
				calcData();
			},
		);
		autoResize(
			scrollBoard,
			onResize,
			afterAutoResizeMixinInit,
			(w: number, h: number) => {
				console.log('-=-=-scrollBoard=-=-', w, h);
				width.value = w;
				height.value = h;
			},
		);

		onMounted(() => {
			stopAnimation();
		});

		return {
			scrollBoard,
			width,
			height,
			mergedConfigR,
			header,
			widths,
			aligns,
			heights,
			rows,
			emitEvent,
			handleHover,
			updateRows,
		};
	},
});
</script>